package com.framework.dubbo.producer.file2html;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import com.github.junrar.Archive;
import com.github.junrar.rarfile.FileHeader;
import org.apache.commons.compress.archivers.ArchiveEntry;
import org.apache.commons.compress.archivers.ArchiveInputStream;
import org.apache.commons.compress.archivers.ArchiveStreamFactory;
import org.apache.commons.compress.archivers.sevenz.SevenZArchiveEntry;
import org.apache.commons.compress.archivers.sevenz.SevenZFile;
import org.apache.commons.compress.compressors.bzip2.BZip2CompressorInputStream;
import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream;

import java.io.*;
import java.util.*;

/**
 * zip文件预览 包含 .zip .tar .tar.gz
 *  带密码的压缩文件不支持
 * @author xianggj
 * @Date 2021/6/7 15:15
 **/
public class ZipFilePreviewUtil extends AbstractFilePreviewTools {

    /**
     * 支持的文件类型
     */
    public static final Map<String, String> SUPPORT_TYPES = new HashMap<>();

    static {
        SUPPORT_TYPES.put(FileType.AR.getValue(), ArchiveStreamFactory.AR);
        SUPPORT_TYPES.put(FileType.ARJ.getValue(), ArchiveStreamFactory.ARJ);
        SUPPORT_TYPES.put(FileType.WAR.getValue(), ArchiveStreamFactory.JAR);
        SUPPORT_TYPES.put(FileType.JAR.getValue(), ArchiveStreamFactory.JAR);
        SUPPORT_TYPES.put(FileType.TAR.getValue(), ArchiveStreamFactory.TAR);
        SUPPORT_TYPES.put(FileType.GZ.getValue(), ArchiveStreamFactory.TAR);
        SUPPORT_TYPES.put(FileType.BZ2.getValue(), ArchiveStreamFactory.TAR);
        SUPPORT_TYPES.put(FileType.ZIP.getValue(), ArchiveStreamFactory.ZIP);
        SUPPORT_TYPES.put(FileType.CPIO.getValue(), ArchiveStreamFactory.CPIO);
    }

    @Override
    public String convert2Html(String path) {
        return compress2Html(path);
    }

    @Override
    public String convert2Html(InputStream in, String ext) {
        return compress2Html(in, ext);
    }

    @Override
    public String convert2Str(InputStream in, String ext) {
        return "";
    }

    /**
     * 压缩文件转html
     * @param path
     * @return
     */
    public String compress2Html(String path) {
        File file = new File(path);
        String name = file.getName();
        String ext = StrUtil.subAfter(name, ".", true).toLowerCase();
        // 7z 文件特殊处理
        if (FileType.SEVEN_Z.getValue().equals(ext)) {
            try {
                List<String> files = seven7View(file);
                return list2Html(files);
            } catch (IOException e) {
                e.printStackTrace();
            }
        } else if (FileType.RAR.getValue().equals(ext)){
            try (InputStream fis = new FileInputStream(file)) {
                List<String> files = rarView(fis);
                return list2Html(files);
            } catch (Exception e) {
                e.printStackTrace();
            }
        } else {
            try (InputStream fis = new FileInputStream(file)) {
                String html = compress2Html(fis, ext);
                return html;
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return "无法解压压缩文件";
    }

    /**
     * 将InputStream写入本地文件
     * @param destination 写入本地目录
     * @param input 输入流
     * @throws IOException IOException
     */
    public void writeToLocal(String destination, InputStream input)
            throws IOException {
        int index;
        byte[] bytes = new byte[1024];
        FileOutputStream downloadFile = new FileOutputStream(destination);
        while ((index = input.read(bytes)) != -1) {
            downloadFile.write(bytes, 0, index);
            downloadFile.flush();
        }
        input.close();
        downloadFile.close();

    }

    /**
     * 压缩文件转html
     * @param in
     * @return
     */
    public String compress2Html(InputStream in, String ext) {
        List<String> files = null;
        if (SUPPORT_TYPES.containsKey(ext)){
            try {
                InputStream inputStream = null;
                if (FileType.GZ.getValue().equals(ext)){
                    inputStream = new GzipCompressorInputStream(in);
                } else if (FileType.BZ2.getValue().equals(ext)) {
                    inputStream = new BZip2CompressorInputStream(in);
                } else {
                    inputStream = in;
                }
                //GB2312是大多数压缩文件的格式 ，但是也有例外, 这里不好处理
                files = fileView(SUPPORT_TYPES.get(ext), inputStream, "GB2312");
            } catch (Exception e) {
                e.printStackTrace();
                return "无法解压压缩文件";
            }
        } else if (FileType.RAR.getValue().equals(ext)){
            try {
                files = rarView(in);
            } catch (Exception e) {
                e.printStackTrace();
                return "无法解压压缩文件";
            }
        } else {// 7z文件不建议这样处理
            String path = staticPath + "test.7z";
            try {
                writeToLocal(path, in);
                files = seven7View(new File(path));
            } catch (IOException e) {
                e.printStackTrace();
                return "无法解压压缩文件";
            } finally {
                FileUtil.del(path);
            }
        }
        return list2Html(files);
    }

    /**
     * 转html
     * @param files
     * @return
     */
    private static String list2Html(List<String> files) {
        if (files == null || files.size() ==0){
            return "";
        }
        StringBuffer sb = new StringBuffer();
        sb.append("<html>");
        sb.append("<head>");
        sb.append("<meta http-equiv='Content-Type' content='text/html; charset=utf-8' />");
        sb.append("</head>");
        sb.append("<body text='#000000'>");
        addTree(sb, assemTree(files));
        sb.append("</body>");
        sb.append("</html>");
        return sb.toString();
    }

    private static List<Tree> assemTree(List<String> files) {
        Map<String, Tree> map = new HashMap<>();
        List<Tree> trees = new ArrayList<>();
        for (int i = 0; i < files.size(); i++) {
            String fname = files.get(i);
            if (StrUtil.endWith(fname, "/")){
                fname = StrUtil.sub(fname, 0 , fname.length() -1);
            }
            String[] splitList = StrUtil.split(fname, "/");
            int index = splitList.length - 1;
            String name = splitList[index];
            Tree t = new Tree(name, index);
            map.put(index + "#"+ name, t);
            if (index > 0){
                String pk = (index - 1) + "#" + splitList[index - 1];
                if (map.containsKey(pk)) {
                    Tree tree = map.get(pk);
                    tree.trees.add(t);
                }
            } else {
                trees.add(t);
            }
        }
        return trees;
    }

    private static void addTree(StringBuffer sb, List<Tree> ts) {
        for (Tree t : ts) {
            sb.append("<ul>|- ");
            sb.append(t.name);
            if (t.trees.size() > 0){
                addTree(sb, t.trees);
            }
            sb.append("</ul>");
        }
    }


    /**
     *  7z文件读取
     * @param file
     * @throws IOException
     */
    private static List<String> seven7View(File file) throws IOException {
        List<String> fileList = new ArrayList<>();
        SevenZFile sevenZFile = new SevenZFile(file);
        SevenZArchiveEntry nextEntry = null;
        while ((nextEntry = sevenZFile.getNextEntry()) != null){
            fileList.add(nextEntry.getName());
        }
        return fileList;
    }

    /**
     *  7z文件读取
     * @throws IOException
     */
    private static List<String> rarView(InputStream in) throws Exception {
        List<String> fileList = new ArrayList<>();
        Archive archive = new Archive(in);
        if(archive != null) {
            FileHeader header = null;
            while ((header = archive.nextFileHeader()) != null) {
                String fileName = header.getFileName();
                fileName = StrUtil.replace(fileName, "\\", "/");
                fileList.add(fileName);
            }
        }
        Collections.reverse(fileList);
        return fileList;
    }

    /**
     * zip文件读取
     * @param in
     * @return
     * @throws Exception
     */
    public static List<String> fileView(String ext, InputStream in, String encodeing) throws Exception {
        List<String> fileList = new ArrayList<>();

        ArchiveInputStream archiveInputStream = new ArchiveStreamFactory()
                .createArchiveInputStream(ext, in, encodeing);
        ArchiveEntry zae = null;
        while ((zae = archiveInputStream.getNextEntry()) != null){
            fileList.add(zae.getName());
        }
        if (archiveInputStream != null){
            try {
                archiveInputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return fileList;
    }

    /**
     * 文件树
     */
    private static  class Tree{

        public Tree(String name, int index) {
            this.name = name;
            this.index = index;
        }

        private String name;

        private int index;

        private List<Tree> trees = new ArrayList<>();

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public List<Tree> getTrees() {
            return trees;
        }

        public void setTrees(List<Tree> trees) {
            this.trees = trees;
        }
        public void addTrees(Tree name) {
            this.trees.add(name);
        }
    }

}


